package com.suray.basic.wcs.utils;

/**
 * CRC16
 */
public class Crc16 {
    /**
     * 约定多项式
     */
    private static final int POLYNOMIAL = 0xA001;

    public static int getCRC16(byte[] bytes) {
        return CRC_XModem(bytes, bytes.length - 4);
    }

    public static int CRC_XModem2(byte[] bytes, int len) {
        // initial value
        int crc = 0xffff;
        for (int index = 0; index < len; index++) {
            byte b = bytes[index];
            for (int i = 0; i < 8; i++) {
                boolean bit = ((b >> (7 - i) & 1) == 1);
                boolean c15 = ((crc >> 15 & 1) == 1);
                crc <<= 1;
                if (c15 ^ bit) {
                    crc ^= POLYNOMIAL;
                }
            }
        }
        crc &= 0xffff;
        return crc;
    }

    public static int CRC_XModem(byte[] bytes, int len) {
        //预置16位crc寄存器，初值全部为1  0xFFFF
        int wcrc = 0xFFFF;
        int temp;                    //定义中间变量
        int i, j;                              //定义计数
        for (i = 0; i < len; i++)                        //循环计算每个数据
        {
            temp = bytes[i] & 0x00FF;                     //将八位数据与crc寄存器亦或
            wcrc ^= temp;                           //将数据存入crc寄存器

            for (j = 0; j < 8; j++)                      //循环计算数据的
            {
                if ((wcrc & 0x0001) == 1)                   //判断右移出的是不是1，如果是1则与多项式进行异或。
                {
                    wcrc >>= 1;                     //先将数据右移一位
                    wcrc ^= POLYNOMIAL;                 //与上面的多项式进行异或
                } else {
                    wcrc >>= 1;                     //直接移出
                }
            }
        }
        // 高位低位反转
        wcrc = ((wcrc & 0x0000FF00) >> 8) | ((wcrc & 0x000000FF) << 8);

        return wcrc;
    }

    private static int UpdateCRC16(int crcIn, byte data)
    {
        int crc = crcIn;
        int in = (data|0x100) & 0x1FF;

        do
        {
            crc <<= 1;
            in <<= 1;

            if((in & 0x100) > 0)
            {
                ++crc;
            }

            if((crc & 0x10000) > 0)
            {
                crc ^= 0x1021; //这个说明用的是 CRC16 x16+x12+x5+1.
                //crc ^= 0x8005;
            }
        } while(!((in & 0x10000) > 0));

        return (crc&0xffff);
    }

    /****************************************************************
     *函数名称:  UpdateCRC16
     *
     *输入参数:  根据多项式进行检验算法计算
     *
     *函数功能：  Source CRT软件中ymode CRC16校验算法
     *
     *返 回 值：  成功：校验码；失败：无
     *
     *特    性： 宽度（16）、多项式（1021）、初始值（0000）、结果异或值（0000）、输入数据反转（未选择）、输出数据反转（未选择）
     *
     *****************************************************************/
    public static int Cal_CRC16(byte[] data, int size)
    {
        int crc = 0;
        int index = 0;

        while(index < size)
        {
            crc = UpdateCRC16(crc, data[index]);
            index++;
        }

        crc = UpdateCRC16(crc, (byte) 0);
        crc = UpdateCRC16(crc, (byte) 0);

        return (crc&0xffff);
    }
}
